package cn.aitrox.ry.service.anno.service.impl;

import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.common.exception.CustomBizException;
import cn.aitrox.ry.constant.CommonConstant;
import cn.aitrox.ry.enumtype.ProcessStatusEnum;
import cn.aitrox.ry.enumtype.RespCodeEnum;
import cn.aitrox.ry.service.anno.config.AnnoNacosProperties;
import cn.aitrox.ry.service.anno.dao.AnnoFileHandlerInfoDao;
import cn.aitrox.ry.service.anno.entity.AnnoFileHandlerInfoEntity;
import cn.aitrox.ry.service.anno.enumtype.FileHandlerBizTypeEnum;
import cn.aitrox.ry.service.anno.mq.msg.FileHandlerMsg;
import cn.aitrox.ry.service.anno.service.FileHandlerInfoService;
import cn.aitrox.ry.service.dwh.dto.DwhImageDto;
import cn.aitrox.ry.service.dwh.dto.in.DwhImageQueryInDto;
import cn.aitrox.ry.service.dwh.feign.DwhImageFeign;
import cn.aitrox.ry.util.ArrayUtil;
import cn.aitrox.ry.util.FileDownLoadUtil;
import cn.aitrox.ry.util.FsUtil;
import cn.aitrox.ry.util.JSONUtil;
import cn.aitrox.ry.util.LogUtil;
import cn.aitrox.ry.util.NetUtil;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
public class FileHandlerInfoServiceImpl implements FileHandlerInfoService {

    private static final Logger LOG = LoggerFactory.getLogger(FileHandlerInfoServiceImpl.class);

    private static final String TARGET_FILE_REGEX = "[^a-zA-Z0-9|\\.]+";

    @Autowired
    private AnnoFileHandlerInfoDao fileHandlerInfoDao;

    @Autowired
    @Lazy
    private DwhImageFeign dwhImageFeign;

    @Autowired
    private AnnoNacosProperties annoNacosProperties;

    private String getLock(Integer taskId, String seriesUid) {
        return StringUtils.joinWith(CommonConstant.ZHONG_HUA_XIAN, "fileHnadlerInfo", taskId, seriesUid).intern();
    }

    @Override
    public RespResult<String> getDeepzoomUrl(Integer taskId, String seriesUid, Boolean returnUrl) {
        RespResult<AnnoFileHandlerInfoEntity> respResult = this.downloadImage4Task(FileHandlerBizTypeEnum.BIG_IMAGE_DOWNLOAD, taskId, seriesUid);
        if (!respResult._isOk()) {
            return RespResult.secByError(respResult.getCode(), respResult.getMsg());
        }

        if (!returnUrl) {
            return RespResult.ok();
        }

        return this.validLevel(respResult.getData().getFileTargetPath());
    }

    @Override
    public void handlerMsg(FileHandlerMsg fileHandlerMsg) {
        AnnoFileHandlerInfoEntity handlerInfoEntity = fileHandlerInfoDao.findById(fileHandlerMsg.getId());
        if (null == handlerInfoEntity) {
            throw new CustomBizException("文件处理记录不存在! id = " + fileHandlerMsg.getId());
        }
        // 暂时只支持文件下载的处理
        RespResult respResult = this.downloadImage(handlerInfoEntity);
        if (!respResult._isOk()) {
            throw new CustomBizException(respResult.getMsg());
        }
    }

    @Override
    public RespResult<AnnoFileHandlerInfoEntity> downloadImage4Task(FileHandlerBizTypeEnum fileHandlerBizTypeEnum, Integer taskId, String seriesUid) {
        AnnoFileHandlerInfoEntity fileHandlerInfo = fileHandlerInfoDao.findOne(new AnnoFileHandlerInfoEntity(null, taskId, seriesUid, fileHandlerBizTypeEnum.getBizType(), null, null, null));
        if (null != fileHandlerInfo && ProcessStatusEnum._isSuccess(fileHandlerInfo.getStatus())) {
            return RespResult.ok(fileHandlerInfo);
        }
        String lock = this.getLock(taskId, seriesUid);
        synchronized (lock) {
            // 查询数仓获取下载地址
            RespResult<DwhImageDto> dwhImageDtoRespResult = this.queryDwh(seriesUid);
            if (!dwhImageDtoRespResult._isOk()) {
                return RespResult.secByError(dwhImageDtoRespResult.getCode(), dwhImageDtoRespResult.getMsg());
            }

            DwhImageDto dwhImageDto = dwhImageDtoRespResult.getData();


            if (null == fileHandlerInfo) {
                // 文件名处理

                String targetFilePath = this.getTargetFilePath(fileHandlerBizTypeEnum, taskId, seriesUid, dwhImageDto.getPath());
                // 下载
                Boolean downloadResult = FsUtil.download(dwhImageDto.getUrlLan(), new File(targetFilePath));
                if (downloadResult) {
                    // 保存 AnnoFileHandlerInfoEntity
                    fileHandlerInfo = new AnnoFileHandlerInfoEntity(null, taskId, seriesUid, fileHandlerBizTypeEnum.getBizType(), ProcessStatusEnum.SUCCESS.getStatus(), targetFilePath, null);
                    fileHandlerInfoDao.insertSelective(fileHandlerInfo);
                } else {
                    return RespResult.secByError(RespCodeEnum.ERROR.getCode(), "下载视频影像失败");
                }

            } else if (!ProcessStatusEnum._isSuccess(fileHandlerInfo.getStatus())) {
                // 下载
                Boolean downloadResult = FsUtil.download(dwhImageDto.getUrlLan(), new File(fileHandlerInfo.getFileTargetPath()));
                if (downloadResult) {
                    fileHandlerInfo.setStatus(ProcessStatusEnum.SUCCESS.getStatus());
                    fileHandlerInfo.setErrMsg(CommonConstant.EMPTY);
                    // 更新状态 AnnoFileHandlerInfoEntity
                    fileHandlerInfoDao.updateByIdSelective(new AnnoFileHandlerInfoEntity(fileHandlerInfo.getId(), null, null, null, ProcessStatusEnum.SUCCESS.getStatus(), null, CommonConstant.EMPTY));
                } else {
                    return RespResult.secByError(RespCodeEnum.ERROR.getCode(), "下载视频影像失败");
                }
            }
        }

        return RespResult.ok(fileHandlerInfo);
    }

    @Override
    public RespResult downloadImage(AnnoFileHandlerInfoEntity fileHandlerInfo) {
        String lock = this.getLock(fileHandlerInfo.getTaskId(), fileHandlerInfo.getSeriesUid());
        synchronized (lock) {
            if (ProcessStatusEnum._isSuccess(fileHandlerInfo.getStatus())) {
                return RespResult.ok();
            }

            // 查询数仓获取下载地址
            RespResult<DwhImageDto> dwhImageDtoRespResult = this.queryDwh(fileHandlerInfo.getSeriesUid());
            if (!dwhImageDtoRespResult._isOk()) {
                return RespResult.secByError(dwhImageDtoRespResult.getCode(), dwhImageDtoRespResult.getMsg());
            }

            DwhImageDto dwhImageDto = dwhImageDtoRespResult.getData();

            // 下载
            Boolean downloadResult = FsUtil.download(dwhImageDto.getUrlLan(), new File(fileHandlerInfo.getFileTargetPath()));
            if (downloadResult) {
                // 更新状态 AnnoFileHandlerInfoEntity
                fileHandlerInfoDao.updateByIdSelective(new AnnoFileHandlerInfoEntity(fileHandlerInfo.getId(), null, null, null, ProcessStatusEnum.SUCCESS.getStatus(), null, CommonConstant.EMPTY));
                return RespResult.ok();
            } else {
                return RespResult.secByError(RespCodeEnum.ERROR.getCode(), "下载视频影像失败");
            }
        }
    }

    @Override
    public String getTargetFilePath(FileHandlerBizTypeEnum fileHandlerBizTypeEnum, Integer taskId, String seriesUid, String resourceKey) {
        String fileProcessPathPrefix;
        switch (fileHandlerBizTypeEnum) {
            case BIG_IMAGE_DOWNLOAD:
                fileProcessPathPrefix = annoNacosProperties.getDeepzoomPreFilePath();
                break;
            case VIDEO_DOWNLOAD:
                fileProcessPathPrefix = annoNacosProperties.getUltrasoundAlgSharePath();
                break;
            default:
                throw new CustomBizException("不支持的业务类型: " + (null == fileHandlerBizTypeEnum ? (null + CommonConstant.EMPTY) : fileHandlerBizTypeEnum.getBizType()));
        }
        return String.format("%s/%d/%s/%s", fileProcessPathPrefix, taskId, seriesUid, this.processDownloadFileNameSuffix(resourceKey));
    }

    public Boolean downloadFile(String seriesUid, String fileUrl, String targetFilePath) {
        File targetFile = new File(targetFilePath);
        if (targetFile.exists()) {
            targetFile.delete();
        }
        try {
            FileDownLoadUtil.downLoad(fileUrl, targetFile.getAbsolutePath(), 1000, 300000);
            return true;
        } catch (IOException e) {
            LOG.error(LogUtil.genException(e, "文件下载失败", "seriesUid", seriesUid, "fileUrl", fileUrl), e);
            return false;
        }
    }

    private RespResult<String> handlerErr(Long fileHandlerInfoId, Integer respCode, String respMsg) {
        AnnoFileHandlerInfoEntity update = new AnnoFileHandlerInfoEntity();
        update.setId(fileHandlerInfoId);
        update.setStatus(ProcessStatusEnum.FAIL.getStatus());
        update.setErrMsg(respMsg);

        fileHandlerInfoDao.updateByIdSelective(update);

        return RespResult.secByError(respCode, respMsg);
    }

    private RespResult<String> validLevel(String targetFilePath) {

        String uri = URLEncoder.encode(targetFilePath.replace(annoNacosProperties.getDeepzoomPreFilePath(), CommonConstant.EMPTY));
        // 判断大图后缀
        Boolean isTmap;
        String fileType = FsUtil.getFileTypeByFilePath(uri).toLowerCase();
        if ("tmap".equals(fileType)) {
            isTmap = true;
        } else {
            isTmap = false;
        }

        // 如果不是是TMAP，则校验层数
        if (!isTmap) {
            String levelCountUrl = StringUtils.joinWith(CommonConstant.EMPTY, annoNacosProperties.getDeepzoomBaseUrlWan(), uri);

            LOG.info(LogUtil.gen("FileHandleResultManager level_count", "getPathologyFileUrl", levelCountUrl));
            Map<String, String> headers = new HashMap<>();
            headers.put("accept", "*/*");
            headers.put("connection", "Keep-Alive");
            headers.put("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");

            try {
                String result = NetUtil.httpGetString(levelCountUrl + ".level_count", null, headers, "");
                JSONObject parseObject = JSONUtil.fromJson(result, JSONObject.class);
                Object levelCount = parseObject.get("levelCount");
                if (null != levelCount && levelCount instanceof Integer) {
                    if (((Integer) levelCount) == 1) {
                        return RespResult.error("该切片解析出现问题，平台暂时不支持标注，请标废片进行获取下个可标序列!");
                    }
                }
            } catch (Exception e) {
                LOG.error(LogUtil.genException(e, "FileHandleResultManager level_count", "getPathologyFileUrl", levelCountUrl), e);
                return RespResult.error("该切片校验层数异常，请联系管理员!");
            }
        }

        if (isTmap) {
            return RespResult.ok(StringUtils.joinWith(CommonConstant.EMPTY, annoNacosProperties.getDeepzoomTmapBaseUrlWan(), uri));
        }
        return RespResult.ok(StringUtils.joinWith(CommonConstant.EMPTY, annoNacosProperties.getDeepzoomBaseUrlWan(), uri));
    }

    private RespResult<DwhImageDto> queryDwh(String seriesUid) {
        RespResult<Map<String, List<DwhImageDto>>> respResult = dwhImageFeign.imageQuery(new DwhImageQueryInDto(Arrays.asList(seriesUid)));
        if (!respResult._isOk()) {
            return RespResult.secByError(RespCodeEnum.ERROR.getCode(), "查询数仓影像失败");
        }
        Map<String, List<DwhImageDto>> map = respResult.getData();
        if (null == map || null == map.get(seriesUid) || ArrayUtil.isEmpty(map.get(seriesUid))) {
            return RespResult.secByError(RespCodeEnum.NOT_EXIST.getCode(), "数仓影像不存在");
        }
        return RespResult.ok(map.get(seriesUid).get(0));
    }

    public String processDownloadFileNameSuffix(String fileResourceKey) {
        String[] pathArray = fileResourceKey.split(CommonConstant.DEFAULT_FILE_SEPARATOR);

        // 对COPY到本地的文件名作处理，去掉特殊字符
        String fileName = pathArray[pathArray.length - 1].replaceAll(TARGET_FILE_REGEX, CommonConstant.EMPTY);
        fileName = fileName.startsWith(CommonConstant.FILE_NAME_SEPARATOR) ? UUID.randomUUID().toString() + fileName : fileName;
        return fileName;
    }
}
